﻿/// Copyright (c) Microsoft Corporation.  All rights reserved.
using System;
using System.Runtime.InteropServices;
using Microsoft.VisualStudio.Shell;
using System.ComponentModel;
using System.Windows.Forms;
using System.Collections;
using System.Collections.Specialized;
using Tcvt.Packages.BuddyBuildServiceProxy;
using System.Collections.Generic;
using System.Text.RegularExpressions;

namespace Tcvt.Packages
{
    [Guid(GuidList.guidTfsBuggerVSPackageCustomMsbuildParametersOptionsString)]
    public class CustomMsbuildParamterOptions : DialogPage
    {
        private MsbuildPropertiesUserControl _msbuildPropertiesUserControl;
        private MsBuildProperty[] _currentProperties = null;

        public CustomMsbuildParamterOptions()
        {
            //if (this.Properties == null) this.Properties = new MsBuildProperty[]{};
            this.InitializeGrid();
        }

        private void InitializeGrid()
        {
            _msbuildPropertiesUserControl = new MsbuildPropertiesUserControl();
            _msbuildPropertiesUserControl.PropertiesGrid.CellValueChanged += new System.Windows.Forms.DataGridViewCellEventHandler(this.PropertiesGrid_CellValueChanged);
            _msbuildPropertiesUserControl.RefreshGrid = RefreshGridFromServer;
            //_msbuildPropertiesUserControl.MouseLeave += new EventHandler(this.MsbuildPropertiesUserControl_MouseLeave);
        }

        public void RefreshGridFromServer()
        {
            // Call web service and refresh grid.
            Utils.LogMessage("CustomMsbuildParamterOptions::RefreshGridFromServer: Enter.");
            this.PopulatePropertiesGrid(true);
        }

        //void MsbuildPropertiesUserControl_MouseLeave(object sender, EventArgs e)
        //{
        //    if (!_msbuildPropertiesUserControl.PropertiesGrid.Enabled)
        //        return;
        //}

        private void PropertiesGrid_CellValueChanged(object sender, DataGridViewCellEventArgs e)
        {
            this.DoCellValueChanged();
        }

        private void DoCellValueChanged()
        {
            if (!_msbuildPropertiesUserControl.PropertiesGrid.Enabled)
                return;

            List<MsBuildProperty> savedProperties = null;
            if (_currentProperties == null || _currentProperties.Length == 0)
                savedProperties = ConvertToList(Properties);
            else
                savedProperties = ConvertToList(_currentProperties);

            for (int i = 0; i < _msbuildPropertiesUserControl.PropertiesGrid.Rows.Count; i++)
            {
                string propertyName = (string)_msbuildPropertiesUserControl.PropertiesGrid.Rows[i].Cells[0].Value; //Name
                string propertyValue = (string)_msbuildPropertiesUserControl.PropertiesGrid.Rows[i].Cells[1].Value; // DefaultValue
                MsBuildProperty property = savedProperties.Find((MsBuildProperty searchProperty) =>
                {
                    return (searchProperty.Name.Equals(propertyName, StringComparison.InvariantCulture));
                });

                if (property != null) // found a match
                {
                    if (string.IsNullOrEmpty(propertyValue)) // no value indicates use default on server. No need for validation.
                    {
                        property.DefaultValue = string.Empty;
                        continue;
                    }

                    string propertyType = property.PropertyDataType;
                    Type propertyDataType = Type.GetType(propertyType, false);

                    object propertyInstance = null;
                    try
                    {
                        propertyInstance = Convert.ChangeType(propertyValue, propertyDataType);
                    }
                    catch (Exception ex)
                    {
                        Utils.LogMessage(string.Format("CustomMsbuildParamterOptions::DoCellValueChanged: Data type validation failed: {0}: Property Name=\"{1}\" - " +
                            "Property Value=\"{2}\" - Configured Data Type=\"{3}\" - Old Value=\"{4}\". " +
                            "Changes will not be saved.",
                            ex.Message, propertyName, propertyValue, propertyType, property.DefaultValue));

                        Utils.ShowErrorMessageBox(string.Format("Data type validation failed: {0}\r\n\r\nProperty Name=\"{1}\"\r\n" +
                            "Property Value=\"{2}\"\r\nConfigured Data Type=\"{3}\"\r\nOld Value=\"{4}\"\r\n\r\n" +
                            "Changes will not be saved.",
                            ex.Message, propertyName, propertyValue, propertyType, property.DefaultValue));
                        return;
                    }

                    if (propertyInstance == null)
                    {
                        Utils.LogMessage(string.Format("CustomMsbuildParamterOptions::DoCellValueChanged: Data type validation failed: Property value is of invalid datatype: Property Name=\"{0}\" " +
                            "- Property Value=\"{1}\" - Configured Data Type=\"{2}\" - Old Value=\"{3}\". Changes will not be saved.",
                            propertyName, propertyValue, propertyType, property.DefaultValue));

                        Utils.ShowErrorMessageBox(string.Format("Data type validation failed: Property value is of invalid datatype.\r\n\r\nProperty Name=\"{0}\"\r\n" +
                            "Property Value=\"{1}\"\r\nConfigured Data Type=\"{2}\"\r\nOld Value=\"{3}\".\r\n\r\n" +
                            "Changes will not be saved.",
                            propertyName, propertyValue, propertyType, property.DefaultValue));
                        return;
                    }
                    else
                    {
                        Utils.LogMessage(string.Format("CustomMsbuildParamterOptions::DoCellValueChanged: Pre-regex validation. Property Name=\"{0}\" " +
                            "- Property Value=\"{1}\" - Converted Value=\"{2}\".",
                            propertyName, property.DefaultValue, propertyInstance));

                        // Let's do extra vaidation if the regular expression is provided.
                        string validationExpression = property.ValidationExpression;

                        if (!string.IsNullOrEmpty(validationExpression))
                        {
                            if (!Regex.IsMatch(propertyValue, validationExpression))
                            {
                                Utils.LogMessage(string.Format("CustomMsbuildParamterOptions::DoCellValueChanged: Regular expression validation failed: Property Name=\"{0}\" " +
                                    "- Property Value=\"{1}\" - Configured Data Type=\"{2}\" - Validation Expression=\"{3}\" - Old value=\"{4}\". " +
                                    "Changes will not be saved.",
                                    propertyName, propertyValue, propertyType, validationExpression, property.DefaultValue));

                                Utils.ShowErrorMessageBox(string.Format("Regular expression validation failed.\r\n\r\nProperty Name=\"{0}\"\r\n" +
                                    "Property Value=\"{1}\"\r\nConfigured Data Type=\"{2}\"\r\nValidation Expression=\"{3}\"\r\nOld value=\"{4}\"\r\n" +
                                    "Changes will not be saved.",
                                    propertyName, propertyValue, propertyType, validationExpression, property.DefaultValue));
                                return;
                            }
                            else
                            {
                                // Override configured value with the one from the persistant storage:
                                property.DefaultValue = propertyValue;
                            }
                        }
                        else
                        {
                            // Override configured value with the one from the persistant storage:
                            property.DefaultValue = propertyValue;
                        }
                    }
                }
            }
            _currentProperties = new MsBuildProperty[savedProperties.Count];
            savedProperties.CopyTo(_currentProperties);

            //this.Properties = gridMsBuildProperties; // persist the changed/corrected version.
        }

        public void PopulatePropertiesGrid()
        {
            this.PopulatePropertiesGrid(false);
        }

        public void PopulatePropertiesGrid(bool refreshFromServer)
        {
            try
            {
                MsBuildProperty[] definedMsBuildProperties = null;
                DataGridViewRow[] rows = null;
                _currentProperties = null;

                // First, initialize the rows collection with the valid/configured values.
                if (refreshFromServer)
                {
                    definedMsBuildProperties = this.GetConfiguredProperties();
                    _currentProperties = definedMsBuildProperties;

                    // If no custom properties were returned from the server, we can return without 
                    // making a change to the grid.
                    if (definedMsBuildProperties == null || definedMsBuildProperties.Length == 0)
                        return;
                }
                else
                    definedMsBuildProperties = this.Properties;

                // Clear the grid and repopulate with the settings.
                _msbuildPropertiesUserControl.PropertiesGrid.Enabled = false; // disable firing events.
                _msbuildPropertiesUserControl.PropertiesGrid.Rows.Clear();
                _msbuildPropertiesUserControl.PropertiesGrid.Enabled = true;

                if (definedMsBuildProperties != null && definedMsBuildProperties.Length > 0)
                {
                    rows = new DataGridViewRow[definedMsBuildProperties.Length];
                    for (int i = 0; i < definedMsBuildProperties.Length; i++)
                    {
                        DataGridViewRow row = new DataGridViewRow();

                        row.Cells.Add(new DataGridViewTextBoxCell());
                        row.Cells.Add(new DataGridViewTextBoxCell());
                        row.Cells.Add(new DataGridViewTextBoxCell());

                        row.Cells[0].Value = definedMsBuildProperties[i].Name;
                        row.Cells[0].ReadOnly = true;
                        row.Cells[1].Value = Utils.MsBuildDecodeForLists(definedMsBuildProperties[i].DefaultValue);
                        row.Cells[0].ReadOnly = false;
                        row.Cells[2].Value = definedMsBuildProperties[i].Description;
                        row.Cells[0].ReadOnly = true;

                        rows[i] = row;
                    }
                }

                // Add the resulting collection of rows to the grid.
                if (rows != null && rows.Length > 0)
                    _msbuildPropertiesUserControl.PropertiesGrid.Rows.AddRange(rows);

                // Make the grid read-only if we can't access the web service or it is not configured.
                if (definedMsBuildProperties == null || definedMsBuildProperties.Length == 0)
                {
                    _msbuildPropertiesUserControl.PropertiesGrid.ReadOnly = true;
                }
                else
                {
                    _msbuildPropertiesUserControl.PropertiesGrid.ReadOnly = false;
                }

                if (refreshFromServer)
                {
                    Utils.ShowInformationMessageBox("The grid was successfully refreshed from the server.\r\n\r\n" +
                        "Click the 'OK' button to save the new settings or the 'Cancel' button to keep your " +
                        "previous settings.");
                }
            }
            catch (Exception ex)
            {
                Utils.LogMessage(string.Format("CustomMsbuildParamterOptions::PopulatePropertiesGrid: Error populating properties grid: Message: {0}", ex.Message), LogType.Error);
                Utils.LogMessage(string.Format("CustomMsbuildParamterOptions::PopulatePropertiesGrid: Error populating properties grid: {0}", ex.StackTrace), LogType.Error);

                if (MiscellaneousOptionsDialog.ShowAlertsUponCustomSettingsFailure)
                {
                    Utils.ShowErrorMessageBox(string.Format("Failed to populate the properties: {0}\r\n\r\n" +
                        "Please make sure the properties are configured correctly on the web server or ask your Buddy Build Web Service " +
                        "administrator if you are not sure.\r\n\r\nNOTE: The saved properties will be deleted.\r\n" +
                        "Please click on the 'Edit Properties...' button again to refresh the properties.",
                        ex.Message));
                }
                this.ClearGridAndDeleteSavedProperties();
            }
        }

        private void ClearGridAndDeleteSavedProperties()
        {
            try
            {
                _msbuildPropertiesUserControl.PropertiesGrid.Rows.Clear();
                this.Properties = null;
            }
            catch (Exception ex)
            {
                Utils.LogMessage(string.Format("CustomMsbuildParamterOptions::ClearGridAndDeleteSavedProperties: Error clearing properties grid: Message: {0}", ex.Message), LogType.Error);
                Utils.LogMessage(string.Format("CustomMsbuildParamterOptions::ClearGridAndDeleteSavedProperties: Error clearing properties grid: {0}", ex.StackTrace), LogType.Error);
            }
        }

        public BuildOptions BuddyBuildOptionsDialog { get; set; }

        public MiscOptions MiscellaneousOptionsDialog { get; set; }

        [TypeConverter(typeof(MsBuildPropertyConverter))]
        public MsBuildProperty[] Properties { get; set; }

        public TextBox PropertiesTextBox { get; set; }

        public Button EditPropertiesButton { get; set; }

        public MsBuildProperty[] GetConfiguredProperties()
        {
            // No need to check the 'BuddyBuildWebServiceUri' setting since it has to be non-empty
            // if BypassBuddyBuildWebService is set to False.
            if (BuddyBuildOptionsDialog.BypassBuddyBuildWebService)
            {
                Utils.ShowInformationMessageBox("'Bypass Buddy Build Web Service' should be set to False in order " +
                    "to fetch custom properties from the web service.");
                return null;
            }

            if (MainForm.SupportsCustomProperties(BuddyBuildOptionsDialog.BuddyBuildWebServiceUri))
            {
                // Get the configured properties by calling the web service.
                // pass the info to the wbe service.
                BuildService buddyBuildService = new BuildService();
                // Get the web service URL from Settings:
                buddyBuildService.Url = BuddyBuildOptionsDialog.BuddyBuildWebServiceUri;
                buddyBuildService.UserAgent = MainForm.UserAgentMagicString;
                buddyBuildService.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials;
                MsBuildProperty[] customProperties = buddyBuildService.GetCustomProperties();

                if (customProperties == null || customProperties.Length == 0)
                {
                    Utils.ShowInformationMessageBox("Could not get custom properties from the server or " +
                        "there are no custom properties configured on the server.");
                }

                return customProperties;
            }
            else
            {
                Utils.LogMessage(string.Format("CustomMsbuildParamterOptions::GetConfiguredProperties: The current version of the Buddy Build Integration web service does not support " +
                    "custom properties definition. Please upgrade to the latest version of the Buddy Build Integration web service in order " +
                    "to leverage the custom properties feature."));

                Utils.ShowErrorMessageBox(string.Format("The current version of the Buddy Build Integration web service does not support " +
                    "custom properties definition. Please upgrade to the latest version of the Buddy Build Integration web service in order " +
                    "to leverage the custom properties feature."));

                return null;
            }
        }

        //public override object AutomationObject
        //{
        //    get
        //    {
        //        return this.Properties;
        //        //return _msbuildPropertiesUserControl;
        //        //return base.AutomationObject;
        //    }
        //}

        protected override IWin32Window Window
        {
            get
            {
                return (IWin32Window)_msbuildPropertiesUserControl;
                //return base.Window;
            }
        }

        protected override void OnApply(PageApplyEventArgs e)
        {
            Utils.LogMessage("CustomMsbuildParamterOptions::OnApply: Enter.");

            this.ApplyChanges();

            base.OnApply(e);
        }

        protected override void OnClosed(EventArgs e)
        {
            Utils.LogMessage("CustomMsbuildParamterOptions::OnClosed: Enter.");
            base.OnClosed(e);
        }

        protected override void OnDeactivate(CancelEventArgs e)
        {
            Utils.LogMessage("CustomMsbuildParamterOptions::OnDeactivate: Enter.");
            // Make sure there are no dirty cells:
            if (this._msbuildPropertiesUserControl.PropertiesGrid.IsCurrentCellInEditMode)
            {
                bool endEdit = this._msbuildPropertiesUserControl.PropertiesGrid.EndEdit();

                // If we fail to end the edit, we need to cancel the navigation/deactivation:
                if (!endEdit)
                {
                    Utils.LogMessage("CustomMsbuildParamterOptions::OnDeactivate: EndEdit = false. Cancelling OnDeactivate().");
                    e.Cancel = true;
                }
            }

            base.OnDeactivate(e);
        }

        private void ApplyChanges()
        {
            try
            {
                if (_currentProperties != null && _currentProperties.Length > 0)
                {
                    this.Properties = _currentProperties;
                }

                if (this.PropertiesTextBox != null)
                {
                    if (_currentProperties != null && _currentProperties.Length > 0)
                        this.PropertiesTextBox.Text = Utils.SerializeConfiguredProperties(_currentProperties);
                    else if (this.Properties != null && this.Properties.Length > 0)
                        this.PropertiesTextBox.Text = Utils.SerializeConfiguredProperties(this.Properties);
                    this.PropertiesTextBox.Enabled = true;
                    this.PropertiesTextBox = null;
                }

                if (this.EditPropertiesButton != null)
                {
                    this.EditPropertiesButton.Enabled = true;
                    this.EditPropertiesButton = null;
                }
            }
            catch (Exception ex)
            {
                Utils.LogMessage(string.Format("CustomMsbuildParamterOptions::OnApply: Error applying changes: Message: {0}", ex.Message), LogType.Error);
                Utils.LogMessage(string.Format("CustomMsbuildParamterOptions::OnApply: Error applying changes: {0}", ex.StackTrace), LogType.Error);
            }
        }

        public static List<MsBuildProperty> ConvertToList(MsBuildProperty[] properties)
        {
            if (properties == null || properties.Length == 0)
                return null;

            List<MsBuildProperty> propertiesList = new List<MsBuildProperty>(properties.Length);
            for (int i = 0; i < properties.Length; i++)
            {
                propertiesList.Add(new MsBuildProperty()
                {
                    Name = properties[i].Name,
                    DefaultValue = properties[i].DefaultValue,
                    Description = properties[i].Description,
                    PropertyDataType = properties[i].PropertyDataType,
                    ValidationExpression = properties[i].ValidationExpression
                });
            }

            return propertiesList;
        }
    }
}
